package com.shuimutong.gutil.common;

import com.shuimutong.guti.bean.FieldParameterClazz;
import com.shuimutong.guti.bean.TwoTuple;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;

/**
 * 对象值填充
 */
public class BeanValueFill {
    /**
     * 填充对象的属性
     * @param clazz
     * @param instance
     * @throws Exception
     */
    public void fillBeanAttributes(Class clazz, Object instance) throws Exception{
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields) {
            if(jump(field)) {
                continue;
            }
            printField(field);
            field.setAccessible(true);
            TwoTuple<Boolean, ?> simpleTypeVal = checkSimpleTypeAndGenerateVal(field.getType());
            if(simpleTypeVal.getA()) {
                field.set(instance, simpleTypeVal.getB());
                continue;
            }
            TwoTuple<Boolean, ?> collectionTypeVal = checkCollectionAndGenerateVal(field);
            if(collectionTypeVal.getA()) {
                field.set(instance, collectionTypeVal.getB());
                continue;
            }
            printField(field);
            TwoTuple<Boolean, ?> valTuple = generateInstanceByClass(field.getType());
            if(valTuple.getA()) {
                field.set(instance, valTuple.getB());
                if(needDeepFill(field.getType())) {
                    fillBeanAttributes(field.getType(), valTuple.getB());
                }
            }
        }
    }

    private TwoTuple<Boolean,?> checkCollectionTypeAndGenerateVal(Class<?> type) {
        boolean setVal = false;
        Object val = null;
        //list类型
        if(type == List.class) {
            setVal = true;
            val = new ArrayList();
        } else if(type == Map.class) {
            setVal = true;
            val = new HashMap();
        }
        return new TwoTuple<>(setVal, val);
    }

    /**
     * 检测基础类型并生成对应的值
     * @param type
     * @return
     */
    private TwoTuple<Boolean,?> checkSimpleTypeAndGenerateVal(Class<?> type) {
        boolean setVal = false;
        Object val = null;
        if(type == boolean.class || type == Boolean.class) {
            val = true;
            setVal = true;
        } else if(type == int.class || type == Integer.class) {
            val = getInt();
            setVal = true;
        } else if(type == long.class || type == Long.class) {
            val = getLong();
            setVal = true;
        } else if(type == double.class || type == Double.class) {
            val = getDouble();
            setVal = true;
        } else if(type == BigDecimal.class) {
            val = getBigDecimal();
            setVal = true;
        } else if(type == String.class) {
            val = getString();
            setVal = true;
        }
        return new TwoTuple<>(setVal, val);
    }

    /**
     * 检测集合类型并生成对应的值
     * @param field
     * @return
     * @throws Exception
     */
    private TwoTuple<Boolean,?> checkCollectionAndGenerateVal(Field field) throws Exception {
        FieldParameterClazz[] parameterClasses = getFieldParameterType(field);
        if(GUtilCommonUtil.checkListEmpty(parameterClasses)) {
            return new TwoTuple<>(false, null);
        }
        //list类型
        if(field.getType() == List.class) {
            List list = new ArrayList();
            FieldParameterClazz parameterClazz = parameterClasses[0];
            TwoTuple<Boolean, ?> listRes = checkParameterAndGenerateVal(parameterClazz);
            if(listRes.getA()) {
                list.add(listRes.getB());
            } else {
                printField(field);
                System.out.println("实例生成未成功:" + field.getName());
            }
            return new TwoTuple<>(true, list);
        } else if(field.getType() == Map.class) {
            Map map = new HashMap();
            Object key = null,val = null;
            TwoTuple<Boolean, ?> keyTuple = checkParameterAndGenerateVal(parameterClasses[0]);
            if(keyTuple.getA()) {
                key = keyTuple.getB();
            }
            TwoTuple<Boolean, ?> valTuple = checkParameterAndGenerateVal(parameterClasses[1]);
            if(valTuple.getA()) {
                val = valTuple.getB();
            } else {
                printField(field);
                val = valTuple.getB();
            }
            if(key != null) {
                map.put(key, val);
            } else {
                printField(field);
                System.out.println("实例生成未成功:" + field.getName());
            }
            return new TwoTuple<>(true, map);
        }
        return new TwoTuple<>(false, null);
    }

    /**
     * 是否需要深度遍历填充对象
     * @param type
     * @return
     */
    public boolean needDeepFill(Class<?> type) {
        String className = type.getName();
        if(className.startsWith("com.shuimutong")) {
            return true;
        }
        return false;
    }

    public boolean jump(Field field) {
        if(Modifier.isFinal(field.getModifiers())) {
            return true;
        }
        return false;
    }

    public TwoTuple<Boolean, ?> generateInstanceByClass(Class<?> clazz) {
        TwoTuple<Boolean,?> typeRes = checkSimpleTypeAndGenerateVal(clazz);
        if(typeRes.getA()) {
            return new TwoTuple<>(true, typeRes.getB());
        }
        typeRes = checkCollectionTypeAndGenerateVal(clazz);
        if(typeRes.getA()) {
            return new TwoTuple<>(true, typeRes.getB());
        }
        if(clazz.isInterface()) {
            return new TwoTuple<>(false, null);
        }
        if(Modifier.isAbstract(clazz.getModifiers())) {
            return new TwoTuple<>(false, null);
        }
        Boolean setVal = false;
        Object t = null;
        try {
            t = clazz.newInstance();
            setVal = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new TwoTuple<>(setVal, t);
    }

    /**
     * 根据泛型类生成对应实例
     * @param parameterClazz
     * @return
     */
    private TwoTuple<Boolean, ?> checkParameterAndGenerateVal(FieldParameterClazz parameterClazz) {
        Boolean setVal = false;
        Object returnVal = null;
        if(parameterClazz == null || parameterClazz.getClazz() == null) {
            return new TwoTuple<>(false, null);
        }
        Class mainClass = parameterClazz.getClazz();
        TwoTuple<Boolean, ?> mainInstance = generateInstanceByClass(mainClass);
        if(!mainInstance.getA()) {
            return new TwoTuple<>(false, null);
        }
        setVal = true;
        returnVal = mainInstance.getB();
        if(!GUtilCommonUtil.checkListEmpty(parameterClazz.getParameterClasses())) {
            FieldParameterClazz[] childParameterClasses = parameterClazz.getParameterClasses();
            if(returnVal instanceof List) {
                FieldParameterClazz itemClazz = childParameterClasses[0];
                TwoTuple<Boolean, ?> listParameterRes = checkParameterAndGenerateVal(itemClazz);
                if(listParameterRes.getA()) {
                    Object listItem = listParameterRes.getB();
                    //当前元素不含泛型类，进行赋值
                    if(GUtilCommonUtil.checkListEmpty(itemClazz.getParameterClasses())) {
                        try {
                            fillBeanAttributes(itemClazz.getClazz(), listItem);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    ((List)returnVal).add(listItem);
                }
            } else if(returnVal instanceof Map) {
                Map tmpMap = (Map) returnVal;
                Object tmpMapKey = null;
                Object tmpMapVal = null;
                FieldParameterClazz keyItemClazz = childParameterClasses[0];
                TwoTuple<Boolean, ?> keyParameterRes = checkParameterAndGenerateVal(keyItemClazz);
                if(keyParameterRes.getA()) {
                    tmpMapKey = keyParameterRes.getB();
                }
                FieldParameterClazz valItemClazz = childParameterClasses[1];
                TwoTuple<Boolean, ?> valParameterRes = checkParameterAndGenerateVal(valItemClazz);
                if(valParameterRes.getA()) {
                    tmpMapVal = keyParameterRes.getB();
                    //当前元素不含泛型类，进行赋值
                    if(GUtilCommonUtil.checkListEmpty(valItemClazz.getParameterClasses())) {
                        try {
                            fillBeanAttributes(valItemClazz.getClazz(), tmpMapVal);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                if(tmpMapKey != null) {
                    tmpMap.put(tmpMapKey, tmpMapVal);
                }
            }
        }
        return new TwoTuple<>(setVal, returnVal);
    }

    /**
     * 获取字段上的泛型
     * @param field
     * @return
     */
    public FieldParameterClazz[] getFieldParameterType(Field field) {
        FieldParameterClazz[] classes = null;
        Type type = field.getGenericType();
        if(type != null && type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            Type[] types = pt.getActualTypeArguments();
            if(!GUtilCommonUtil.checkListEmpty(types)) {
                classes = new FieldParameterClazz[types.length];
                for(int i=0; i<types.length; i++) {
                    Type tmpType = types[i];
                    classes[i] = getParameterImp(tmpType);
                }
            }
        }
        return classes;
    }

    public FieldParameterClazz getParameterImp(Type type) {
        FieldParameterClazz fieldParameterClazz = null;
        if(type instanceof Class) { //普通类
            Class tmpClass = (Class) type;
            fieldParameterClazz = new FieldParameterClazz(tmpClass);
        } else if(type instanceof WildcardTypeImpl) { //问号类型的属于这个类
            fieldParameterClazz = new FieldParameterClazz(Object.class);
        } else { //包含有泛型的类
            ParameterizedTypeImpl typeImpl = (ParameterizedTypeImpl) type;
            Type ownerType = typeImpl.getRawType();
            Type[] types = typeImpl.getActualTypeArguments();
            if(GUtilCommonUtil.checkListEmpty(types)) {
                fieldParameterClazz = new FieldParameterClazz((Class)ownerType);
            } else {
                FieldParameterClazz[] childParameters = new FieldParameterClazz[types.length];
                fieldParameterClazz = new FieldParameterClazz((Class)ownerType, childParameters);
                for(int i=0; i<types.length; i++) {
                    childParameters[i] = getParameterImp(types[i]);
                }
            }
        }
        return fieldParameterClazz;
    }



    private void printField(Field field) {
        System.out.println(field.getName() + "----" + field.getType());
    }

    static Random random = new Random();

    private int getNum() {
        return Math.abs(random.nextInt());
    }

    private int getInt() {
        return getNum();
    }
    private long getLong() {
        return getNum() * 10000 + getNum();
    }
    private double getDouble() {
        return random.nextDouble();
    }
    private BigDecimal getBigDecimal() {
        return BigDecimal.valueOf(getDouble());
    }
    private String getString() {
        return "str" + getNum();
    }

}
